#include <unistd.h>
#include <jni.h>
#include <string>
#include <android/log.h>
#include "User.h"
#include "JniTools.h"

#define LOG_TAG    "C_TAG"
#define LOGD(...)  __android_log_print  (ANDROID_LOG_DEBUG,  LOG_TAG, __VA_ARGS__)



extern "C"
jstring
Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_userLOGDn(
        JNIEnv *env,
        jobject  , jstring userName , jstring userPwd) {
    LOGD("执行native代码了 ");
    User user (userName , userPwd )   ;
    jstring userInfo =   user.getUserInfo(env);
    return  userInfo;
}

extern "C"
jstring
Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_userLogin(
        JNIEnv *env,
        jobject  , jstring userName , jstring userPwd) {
    LOGD("执行native代码了 ");
    User user (userName , userPwd )   ;
    jstring userInfo =   user.getUserInfo(env);
    return  userInfo;
}


extern "C"
jint Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_goToNative( JNIEnv *env,   jobject  , jint value)
{
    //加载java 本地定义的类
    jclass  myJavaClass = env ->FindClass( "aivin/android_jni_cpp/jniTool/MyJniTools");
    //方法签名的规则 ：   （参数类型签名） + 返回值类型签名
    //要注意参数的签名，否则会报错 找不到对应的方法
    jmethodID methodID = env ->GetMethodID(  myJavaClass, "calledByNativeC", "(IILjava/lang/String;)I");
    //实例化该类  jobject 表示类实例。
    jobject obj = env ->AllocObject( myJavaClass);
    //  java中 方法传入的参数。value就是java中方法的返回值
    std::string hello = " 来自native的信息 ";
    jint resultValue = env ->CallIntMethod(  obj, methodID, value,   99 , env->NewStringUTF(hello.c_str()));
    LOGD("c收到经过Java端反馈的结果 =%d"  ,resultValue) ;
    return   resultValue ;

}


extern "C"
jint Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_test( JNIEnv *env, jobject  , jstring name)
{
    //jni版本号
    //高16位返回主版本号，低16位返回次版本号,如在JDK/JRE 1.6中，
    // 返回0x00010006。也有可能返回 JNI_EDETACHED 和 JNI_EVERSION 错误码
    jint version =   env->GetVersion() ;
    LOGD("version=%d " , version);

    // 字符数组 转 jstring
    std::string hello = "hello world ,from native" ;
    jstring  jStr= env->NewStringUTF(hello.c_str()) ;

    // 获取 jstring   UTF-8格式 的长度
    jint lengh = env->GetStringUTFLength(jStr) ;
    LOGD("lengh=%d" , lengh) ;
    // 获取 jstring   Unicode格式的 长度
    jint length1 =env->GetStringLength(jStr) ;

    //jstr 转 UTF-8格式的 char*
    /*
     isCopy 参数：
     NULL，则表示不关心是否拷贝字符串  ;
     JNI_FALSE，则直接返回指向Java中String的内存指针;
     JNI_TRUE 则表示开辟内存，然后把Java中的String拷贝到这个内存中
     */

    const char*  mChar = env->GetStringUTFChars(jStr , NULL) ;
    LOGD("mChar=%s" , mChar) ;
    //jstring 转 Unicode格式的char*
    const  jchar *  mCharUnicode = env->GetStringChars(jStr , NULL) ;

    // 释放 jstring  ??
    //env->ReleaseStringUTFChars(jStr  ,mChar) ;
    //释放 jstring jchar  ？？
    //env->ReleaseStringChars(jStr , mCharUnicode) ;
}
extern "C"
void Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_getJavaFile( JNIEnv *env, jobject  jobj   )
{
//    jclass jclazz = env->GetObjectClass(jobj);
    //获取类
    jclass jclazz = env->FindClass("aivin/android_jni_cpp/jniTool/MyJniTools");

    //获取静态属性
    jfieldID fid = env->GetStaticFieldID(jclazz, "rootName", "Ljava/lang/String;");
    //获取静态属性的值
    jstring name = (jstring) env->GetStaticObjectField(jclazz, fid);

    const char* oldName = env->GetStringUTFChars(name, NULL);
    LOGD("静态属性的值 =%s"  ,  oldName) ;
    //修改值
    char *appendStr=" 修改标记" ;
    char *reslut = (char*) malloc( strlen(oldName) + strlen(appendStr) + 1 );
    strcpy( reslut , oldName) ;
    strcat(reslut , appendStr) ;

    // 将jstring类型的变量，设置到java
    jstring last= env->NewStringUTF( reslut );
    env->SetStaticObjectField(jclazz, fid, last);

    //释放字符串
    env->ReleaseStringUTFChars(name ,oldName ) ;
    env->ReleaseStringUTFChars(last ,reslut ) ;
}



int isContinue =1 ;
jint progress = 0 ;




extern "C"
void
Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_startProgress(JNIEnv *env, jclass type)
{
    isContinue = 1 ;
    progress = 0 ;
    //加载java 本地定义的类
    jclass  myJavaClass = env ->FindClass( "aivin/android_jni_cpp/jniTool/MyJniTools");
    //方法签名的规则 ：   （参数类型签名） + 返回值类型签名
    //要注意参数的签名，否则会报错 找不到对应的方法
    jmethodID methodID = env ->GetMethodID(  myJavaClass, "showResultFromNativeC", "(I)V");
    //实例化该类  jobject 表示类实例。
    jobject obj = env ->AllocObject( myJavaClass);

    while (isContinue)
    {
        if(progress==100)
        {
            isContinue = 0 ;
            progress= 0 ;
            break ;
        }
        progress ++ ;
        env ->CallVoidMethod(  obj, methodID, progress  );
        usleep( 1000 *    50) ;// 微秒  //  sleep(1);//休眠 n秒
    }

}



extern "C"
JNIEXPORT jbyteArray JNICALL
Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_processArray(JNIEnv *env, jobject instance,  jbyteArray mBytes_)
{
    //获取原来的bye[]
    jbyte* receivedBytes = env->GetByteArrayElements(mBytes_, NULL);
    jint legth = env->GetArrayLength(mBytes_) ; //数组长度


    for(int i=0; i<legth ;i++)
    {
        jbyte value =  jbyte ( receivedBytes[i] +100); //重新计算

        LOGD("value=%d" , value) ;
        env->SetByteArrayRegion(mBytes_, i, 1, &value); //修改回数组中
    }
    //释放数组内存(不能释放？ 释放后返回后的数据不对了 )
    //env->ReleaseByteArrayElements(mBytes_, receivedBytes, 0);
    //返回修改过的数组
    return mBytes_ ;
};


extern "C"
JNIEXPORT jobject JNICALL
Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_getCarWithArgs(JNIEnv *env, jobject instance,  jstring carType_, jint price)
{



    //创建一个class的引用,使用类的全包名
    jclass cls =  env  -> FindClass(  "aivin/android_jni_cpp/bean/Car");
    //注意这里方法的名称是 "<init>" ，它表示这是一个构造函数
    jmethodID id = (env)->GetMethodID( cls, "<init>",  "(Ljava/lang/String;I)V");
    //获得一实例
    jobject obj = env->NewObject(cls, id, carType_, price);
     return obj;
}




JNIEXPORT jstring JNICALL
Java_aivin_android_1jni_1cpp_jniTool_MyJniTools_checkPackageNameIsLegal(JNIEnv *env,
                                                                        jobject obj,
                                                                        jstring helloworld_) {
    //这个代码为什么这样会报错 。  JNI DETECTED ERROR IN APPLICATION: JNI CallObjectMethodV called with pending exception 'java.lang.NoSuchMethodError' thrown in
    //但是直接 在activity中 调用 so就不会错 。 或者是因为我的包名有下划线的原因
    jclass native_clazz = env->GetObjectClass(obj);
    //获取包名
    jmethodID methodID_packagename =env->GetMethodID(native_clazz,"getPackageName", "()Ljava/lang/String;");
    jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, methodID_packagename));
    return name_str;
}



extern "C"
JNIEXPORT jstring JNICALL
Java_aivin_android_1jni_1cpp_MainActivity_checkPackageNameIsLegal(JNIEnv *env, jobject obj) {

    jclass native_clazz = env->GetObjectClass(obj);
    //获取包名
    jmethodID methodID_packagename =env->GetMethodID(native_clazz,"getPackageName", "()Ljava/lang/String;");
    jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, methodID_packagename));
    return name_str;
}


//合法的APP包名
const char *global_app_packageName = "aivin.android_jni_cpp";
//合法的hashcode - 这个值可以用android api获取
const int global_app_signature_hash_code = -260363308;

extern "C"
JNIEXPORT jint JNICALL
Java_aivin_android_1jni_1cpp_MainActivity_checkApkSingLegalByHashCode(JNIEnv *env,  jobject thiz)
{

    jboolean hasException;

    //获取包名
    jstring jstr_packageName = (jstring) JNU_CallMethodByName( env , &hasException, thiz, "getPackageName", "()Ljava/lang/String;").l;

    if (env->ExceptionCheck( ) || jstr_packageName == NULL)
    {
        LOGD("can't get jstr of getPackageName");
        return -1;
    }

    //获取包名的字符串
    const char* loc_str_app_packageName = env->GetStringUTFChars(   jstr_packageName, NULL);
    if (loc_str_app_packageName == NULL)
    {
        LOGD("can't get packagename from jstring");
        return -2;
    }


    //当前应用包名与合法包名对比
    if (strcmp(loc_str_app_packageName, global_app_packageName) != 0)
    {
        LOGD("this app is illegal");
        return -3;
    }

    //释放loc_str_app_packageName
    env->ReleaseStringUTFChars( jstr_packageName,   loc_str_app_packageName);

    // 获得应用包的管理器
    jobject package_manager = JNU_CallMethodByName(  env ,&hasException, thiz, "getPackageManager", "()Landroid/content/pm/PackageManager;").l;
    if (env->ExceptionCheck( ) || package_manager == NULL)
    {
        LOGD("can't get obj of getPackageManager");
        return -4;
    }

    // 获得应用包的信息
    jobject package_info = JNU_CallMethodByName(env ,  &hasException, package_manager, "getPackageInfo",
                                                "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;",
                                                env->NewStringUTF(  global_app_packageName), 64).l;
    if (env->ExceptionCheck( ) || package_info == NULL)
    {
        env->ExceptionClear( );
        LOGD("can't get obj of package_info");
        return -5;
    }

    // 获得 PackageInfo 类
    jclass pi_clazz = env->GetObjectClass(  package_info);

    // 获得签名数组属性的 ID
    jfieldID fieldID_signatures = env->GetFieldID(  pi_clazz,  "signatures", "[Landroid/content/pm/Signature;");
    env->DeleteLocalRef(  pi_clazz);
    // 得到签名数组
    jobjectArray signatures =(jobjectArray) env->GetObjectField(  package_info,    fieldID_signatures);

    if (env->ExceptionCheck( ) || signatures == NULL)
    {
        LOGD("can't get jobjectArray of signatures");
        return -6;
    }

    // 得到签名
    jobject signature = env->GetObjectArrayElement(  signatures, 0);
    if (env->ExceptionCheck( ) || signature == NULL)
    {
        LOGD("can't get obj of signature");
        return -7;
    }
    //获取当前应用hashcode
    int hash_code = JNU_CallMethodByName( env , &hasException, signature,   "hashCode", "()I").i;


    if (env->ExceptionCheck( ) || package_manager == NULL)
    {
        LOGD("can't get hash_code of signature");
        return -8;
    }


    //合法返回1,否则返回0,并改变legitimate的值
    int legitimate = (hash_code == global_app_signature_hash_code)  ;
    return hash_code ;
//    return legitimate ;

}


